import { Body, Controller, Get, Request, Param, Post, Put, UsePipes, Delete, Req } from '@nestjs/common';
import { isNumber } from 'class-validator';
import { PermissionRole } from 'src/entities/PermissionRole';
import { PermissionRule } from 'src/entities/PermissionRule';
import { SystemMenu } from 'src/entities/SystemMenu';
import { AdminModelService } from 'src/model/admin-model/admin-model.service';
import { MerchantModelService } from 'src/model/merchant-model/merchant-model.service';
import { PermissionRoleModelService } from 'src/model/permission-role-model/permission-role-model.service';
import { PermissionRuleModelService } from 'src/model/permission-rule-model/permission-rule-model.service';
import { SystemMenuModelService } from 'src/model/system-menu-model/system-menu-model.service';
import dtoValidationPipe from 'src/pipe/Dto.pipe';
import { errorReturn, successReturn } from 'src/utils';
import { In } from 'typeorm';
import { AddMenuDto, DeleteMenuDto, EditMenuDto } from './dto/menu.dto';
import { AddRoleDto, DeleteRoleDto, EditRoleDto } from './dto/role.dto';
import { AddRuleDto, DeleteRuleDto, EditRuleDto } from './dto/rule.dto';

@Controller('system')
export class SystemController {
    constructor(
        private systemMenuModelService:SystemMenuModelService,
        private permissionRuleModelService:PermissionRuleModelService,
        private permissionRoleModelService:PermissionRoleModelService,
        private merchant:MerchantModelService,
        private admin:AdminModelService
    ){}

    @Get('permissionResource')
    async permissionResource(@Req() {isPlatform, roleType, userId}) {
        const menus = []
        const rules = []
        let roleIds;


        const allMenus = await this.systemMenuModelService.find(null, null, null, null, {sort : 'DESC', id : 'ASC'})

        if(isPlatform) {
            const admin = await this.admin.findOne({adminId : userId}, ['roles', 'super'])
            if(admin.super) {
                menus.push(...allMenus)

                return {
                    menus,
                    rules
                }
            }

            roleIds = admin.roles
            
        }else {
            const merchant = await this.merchant.findOne({merchantId : userId}, ['roles'])
            roleIds = merchant.roles
        }

        if(!roleIds || roleIds.length == 0) return null

        

        const roles = await this.permissionRoleModelService.find({id : In(roleIds)}, ['menus', 'rules'])
        if(roles && roles.length > 0) {
            const menuIds = []
            const ruleIds = []

            roles.forEach(role => {
                const menus = role.menus as number[]
                if(menus && menus.length > 0) {
                    menuIds.push(...menus)
                }

                const rules = role.rules as number[]
                if(rules && rules.length > 0) {
                    ruleIds.push(...rules)
                }
            })

            if(menuIds.length > 0) {
                const rows = await this.systemMenuModelService.find({id : In(Array.from(new Set(menuIds)))}, null, null, null, {sort : 'DESC', id : 'ASC'})
                const parentIds = []
                const ids = []
                rows.forEach(row => {
                    ids.push(row.id)
                    parentIds.push(row.parentId)
                })
                
                allMenus.forEach(menu => {
                    if(!ids.includes(menu.id) && parentIds.includes(menu.id)) {
                        rows.push(menu)
                    }
                })

                menus.push(...rows)
            }

            if(ruleIds.length > 0) {
                const rows = await this.permissionRuleModelService.find({id : In(Array.from(new Set(ruleIds)))}, ['code'])
                rules.push(...rows.map(row =>  row.code))
            }

        }

        return {
            menus,
            rules
        }
    }

    @Post('menu/add')
    @UsePipes(dtoValidationPipe())
    async addMenu(@Body() dto:AddMenuDto) {
        //判断是否存在了相同的菜单名
        if(true == await this.systemMenuModelService.isExistsByWhere({name : dto.name, parentId : dto.parentId})) {
            return errorReturn('已存在相同的菜单名')
        }

        //判断是否存在了相同的路径
        if(true == await this.systemMenuModelService.isExistsByWhere({path : dto.path, parentId : dto.parentId})) {
            return errorReturn('已存在相同的路径')
        }

        await this.systemMenuModelService.insert({...dto})
        return successReturn()
    }

    @Put('menu/edit/:id')
    @UsePipes(dtoValidationPipe({skipMissingProperties : true}))
    async editMenu(@Param('id') id:number, @Body() dto:EditMenuDto) {
        id = Number(id)
        if(!isNumber(id) || id <= 1) return errorReturn()

        const fields = Object.keys(dto) as (keyof SystemMenu)[]
        if(!fields.includes('parentId')) {
            fields.push('parentId')
        }

        const menu = await this.systemMenuModelService.findOne({id : id}, fields)
        if(!menu) return  errorReturn()

        let updateData:{[key in keyof SystemMenu]? : SystemMenu[key]} = {

        }

        for(let k in dto) {
            if(JSON.stringify(menu[k]) != JSON.stringify(dto[k])) {
                updateData[k] = dto[k]
            }
        }

        if(Object.keys(updateData).length == 0) return successReturn()

        if(updateData.name) {
            if(true == await this.systemMenuModelService.isExistsByWhere({name : updateData.name, parentId : menu.parentId})) {
                return errorReturn('已存在相同的菜单名')
            }
        }

        if(updateData.path) {
            if(true == await this.systemMenuModelService.isExistsByWhere({name : updateData.path, parentId : menu.parentId})) {
                return errorReturn('已存在相同的路径')
            }
        }

        let affectedRows = await this.systemMenuModelService.update({id : id}, updateData)
        
        return affectedRows > 0 ? successReturn() : errorReturn()
    }

    @Delete('menu/delete')
    async deleteMenu(@Body() dto : DeleteMenuDto) {
        //判断是否有子菜单
        if(true == await this.systemMenuModelService.isExistsByWhere({parentId : dto.id})) {
            return errorReturn('当前菜单存在子菜单，不可删除')
        }
        let affectedRows = await this.systemMenuModelService.delete(dto.id)
        //删除权限
        this.permissionRuleModelService.delete({menuId : dto.id})
        return affectedRows > 0 ? successReturn() : errorReturn('dddd')
    }


    @Get('rules/:menuId')
    async getRule(@Param('menuId') menuId:number) {
        return await this.permissionRuleModelService.find({menuId : Number(menuId)})
    }

    @Post('rule/add')
    @UsePipes(dtoValidationPipe())
    async addRule(@Body() dto:AddRuleDto) {
        //判断是否存在了相同的权限名
        if(true == await this.permissionRuleModelService.isExistsByWhere({name : dto.name, menuId : dto.menuId})) {
            return errorReturn('已存在相同的权限名')
        }

        //判断是否存在了相同的编码
        if(true == await this.permissionRuleModelService.isExistsByWhere({code : dto.code, menuId : dto.menuId})) {
            return errorReturn('已存在相同的编码')
        }

        await this.permissionRuleModelService.insert({...dto})
        return successReturn()
    }


    @Put('rule/edit/:id')
    @UsePipes(dtoValidationPipe({skipMissingProperties : true}))
    async editRule(@Param('id') id:number, @Body() dto:EditRuleDto) {
        id = Number(id)
        if(!isNumber(id) || id <= 1) return errorReturn()

        const fields = Object.keys(dto) as (keyof PermissionRule)[]
        if(!fields.includes('menuId')) {
            fields.push('menuId')
        }

        const rule = await this.permissionRuleModelService.findOne({id : id}, fields)
        if(!rule) return  errorReturn()

        let updateData:{[key in keyof PermissionRule]? : PermissionRule[key]} = {

        }

        for(let k in dto) {
            if(JSON.stringify(rule[k]) != JSON.stringify(dto[k])) {
                updateData[k] = dto[k]
            }
        }

        if(Object.keys(updateData).length == 0) return successReturn()

        if(updateData.name) {
            if(true == await this.permissionRuleModelService.isExistsByWhere({name : updateData.name, menuId : rule.menuId})) {
                return errorReturn('已存在相同的名字')
            }
        }

        if(updateData.code) {
            if(true == await this.permissionRuleModelService.isExistsByWhere({code : updateData.code, menuId : rule.menuId})) {
                return errorReturn('已存在相同的编号')
            }
        }

        let affectedRows = await this.permissionRuleModelService.update({id : id}, updateData)
        
        return affectedRows > 0 ? successReturn() : errorReturn()
    }


    @Delete('rule/delete')
    async deleteRule(@Body() dto : DeleteRuleDto) {
        let affectedRows = await this.permissionRuleModelService.delete(dto.ids)
        return affectedRows > 0 ? successReturn() : errorReturn()
    }


    //角色列表
    @Get('roles')
    async getRoles(@Req() {roleType, userId}) {
        return await this.permissionRoleModelService.find({source : roleType, creatorId : userId})
    }

    //
    @Get('selectedRoles')
    async selectedRoles(@Req() {roleType, userId}) {
        return await this.permissionRoleModelService.find({source : roleType, creatorId : userId}, ['name', 'id'])
    }

    @Post('role/add')
    @UsePipes(dtoValidationPipe())
    async addRole(@Body() dto:AddRoleDto, @Req() { roleType, userId}) {
        const params = {creatorId : userId, source : roleType}
        if(true == await this.permissionRoleModelService.isExistsByWhere({name : dto.name, ...params})) {
            return errorReturn('已存在相同的名字')
        }
        await this.permissionRoleModelService.insert({...dto, ...params})
        return successReturn()
    }

    @Put('role/edit/:id')
    @UsePipes(dtoValidationPipe({skipMissingProperties : true}))
    async editRole(@Param('id') id:number, @Body() dto:EditRoleDto) {
        id = Number(id)
        if(!isNumber(id) || id <= 1) return errorReturn()

        const fields = Object.keys(dto) as (keyof PermissionRole)[]
        if(!fields.includes('id')) {
            fields.push('id')
        }

        const rule = await this.permissionRoleModelService.findOne({id : id}, fields)
        if(!rule) return  errorReturn()

        let updateData:{[key in keyof PermissionRole]? : PermissionRole[key]} = {

        }

        for(let k in dto) {
            if(JSON.stringify(rule[k]) != JSON.stringify(dto[k])) {
                updateData[k] = dto[k]
            }
        }

        if(Object.keys(updateData).length == 0) return successReturn()

        if(updateData.name) {
            if(true == await this.permissionRoleModelService.isExistsByWhere({name : updateData.name, id : rule.id})) {
                return errorReturn('已存在相同的名字')
            }
        }

        let affectedRows = await this.permissionRoleModelService.update({id : id}, updateData)
        
        return affectedRows > 0 ? successReturn() : errorReturn()
    }

    @Delete('role/delete')
    async deleteRole(@Body() dto : DeleteRoleDto) {
        let affectedRows = await this.permissionRoleModelService.delete(dto.ids)
        return affectedRows > 0 ? successReturn() : errorReturn()
    }

}
